﻿using JESAI.Redis.Abstracts;
using JESAI.Redis.Abstracts.RedisClients;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace JESAI.Redis.Pools
{
    internal class ObjectInstancePool<T> : IObjectInstancePool<T> where T : IObjectPool
    {
        private Func<Action<T>, CancellationToken, Task<T>> CreateNewObjectFunc;

        private Func<CancellationToken, T> GetObjectFunc;

        private Func<bool> ReturnPreCheckObjIsValidFunc;

        /// <summary>
        /// 空闲的对象实例池
        /// </summary>
        private readonly ConcurrentQueue<T> _freeObjInstances = new();

        /// <summary>
        /// 空闲的数量
        /// </summary>
        private int _freeCount = 0;

        /// <summary>
        /// 池容器最小数量
        /// </summary>
        private readonly int _minSize;

        /// <summary>
        /// 池容器最大数量
        /// </summary>
        private readonly int _maxSize;

        /// <summary>
        /// 当前正在使用的数量
        /// </summary>
        private int _useCount = 0;

        /// <summary>
        /// 当前空闲的数量
        /// </summary>
        private int _idleCount = 0;

        /// <summary>
        /// 闲置时间，超过时间释放
        /// </summary>
        private readonly TimeSpan _idleTime;

        /// <summary>
        /// 尝试获取次数
        /// </summary>
        private readonly int _tryGetRetryCount = 0;

        private Timer releaseTimer;

        public ObjectInstancePool(int minSize, int maxSize, int tryGetRetryCount, TimeSpan idleTime)
        {
            _useCount = 0;
            _minSize = minSize;
            _maxSize = maxSize;
            _idleTime = idleTime;
            _tryGetRetryCount = tryGetRetryCount;
        }

        public ObjectInstancePool(int minSize = 1, int maxSize = 10, int tryGetRetryCount = 5)
        {
            _useCount = 0;
            _minSize = minSize;
            _maxSize = maxSize;
            _tryGetRetryCount = tryGetRetryCount;
            _idleTime = TimeSpan.FromSeconds(60);
        }

        public ObjectInstancePool()
        {
            _useCount = 0;
            _minSize = 1;
            _maxSize = 10;
            _tryGetRetryCount = 5;
            _idleTime = TimeSpan.FromSeconds(60);
        }

        public async Task<T> GetAsync(CancellationToken cancellationToken = default)
        {
            int tryCount = 0;

            while (tryCount < _tryGetRetryCount)
            {
                //线程已经被取消
                if (cancellationToken.IsCancellationRequested)
                {
                    break;
                }

                if (_freeObjInstances.TryDequeue(out T t))
                {
                    Interlocked.Decrement(ref _freeCount); //空闲减一
                    Interlocked.Increment(ref _useCount); //使用加一
                    try
                    {
                        if (GetObjectFunc != null)
                        {
                            t = GetObjectFunc.Invoke(cancellationToken);
                            return t;
                        }
                    }
                    catch (Exception ex)
                    {
                        Interlocked.Decrement(ref _useCount);
                        throw;
                    }
                    return t;
                }

                //判断是否超出对象池的数量，到达的话，则等待
                if (Interlocked.Increment(ref _useCount) <= _maxSize)
                {
                    //创建新的连接
                    try
                    {
                        if (CreateNewObjectFunc != null)
                        {
                            t = await CreateNewObjectFunc.Invoke(this.Return, cancellationToken);
                            return t;
                        }
                    }
                    catch (Exception ex)
                    {
                        Interlocked.Decrement(ref _useCount);
                        throw;
                    }
                }

                //归还
                Interlocked.Decrement(ref _useCount);

                await Task.Delay(100, cancellationToken);
            }

            throw new OperationCanceledException("没用空闲的连接!");
        }

        public void Start()
        {
            if (CreateNewObjectFunc == null)
            {
                throw new InvalidOperationException("请设置CreateNewObjectFunc!");
            }

            new Thread(InitAsync).Start();
            new Thread(ReleaseIdlePoolAsync).Start();
        }

        public void SetCreateNewObjectFunc(Func<Action<T>, CancellationToken, Task<T>> func)
        {
            CreateNewObjectFunc = func;
        }

        public void SetGetObjectFunc(Func<CancellationToken, T> func)
        {
            GetObjectFunc = func;
        }

        public void SetReturnPreCheckObjIsValidFunc(Func<bool> func)
        {
            ReturnPreCheckObjIsValidFunc = func;
        }

        private async void ReleaseIdlePoolAsync()
        {
            if (_idleTime == null) //如果没有设置闲置时间，则不启动定时释放线程
            {
                return;
            }

            releaseTimer = new Timer(_ =>
            {
                //只有超过 最小数才裁剪
                var excessCount = _useCount - _minSize;
                if (excessCount <= 0)
                {
                    return;
                }

                //获取当前时间戳
                if (_freeObjInstances.IsEmpty) return;

                for (var i = 0; i < excessCount; i++)
                {
                    //获取最开始的数据 并且 判断更新时间 是否为合理的时间
                    if (_freeObjInstances.TryPeek(out var t) && (DateTime.Now - t.LastUseTime).TotalMilliseconds >= _idleTime.TotalMilliseconds)
                    {
                        // 读取
                        if (!_freeObjInstances.TryDequeue(out var t2))
                            continue;
                        //判断是否为同一对象
                        if (!RuntimeHelpers.Equals(t2, t))
                        {
                            //还原
                            _freeObjInstances.Enqueue(t2);
                            continue;
                        }

                        t2.Dispose();

                        Interlocked.Decrement(ref _freeCount);
                        Interlocked.Decrement(ref _useCount);
                    }
                    else
                    {
                        releaseTimer.Change(Timeout.Infinite, 0);
                    }
                }
            }, null, Timeout.Infinite, (int)_idleTime.TotalMilliseconds);

            releaseTimer.Change(0, (int)_idleTime.TotalMilliseconds);
        }


        private async void InitAsync()
        {
            for (var i = 0; i < _minSize; i++)
            {
                try
                {
                    if (CreateNewObjectFunc != null)
                    {
                        var t = await CreateNewObjectFunc.Invoke(this.Return, default);
                        //递增
                        Interlocked.Increment(ref _freeCount);
                        _freeObjInstances.Enqueue(t);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }

        /// <summary>
        /// 归还
        /// </summary>
        /// <param name="t"></param>
        private void Return([NotNull] T t)
        {
            //使用减一
            Interlocked.Decrement(ref _useCount);
            //归还前是否需要做些什么事情
            if (ReturnPreCheckObjIsValidFunc != null)
            {
                var result = ReturnPreCheckObjIsValidFunc.Invoke();
                if (!result)
                {
                    t.Dispose();
                    return;
                }
            }
            Interlocked.Increment(ref _freeCount);//空闲加一
            //归还
            _freeObjInstances.Enqueue(t);
        }

        #region Dispose
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Dispose(bool isDispose)
        {
            if (!isDispose) return;
            while (_freeObjInstances.TryDequeue(out var obj))
            {
                obj.Dispose();

            }
            releaseTimer.Dispose();
        }

        #endregion
    }
}
